package com.geek.gkfood.framework.redis;

import com.geek.gkfood.framework.dispatch.SpringCtxUtils;
import com.geek.gkfood.util.json.JacksonUtils;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <h1>操作 Redis 普通 API 的工具类。</h1><br/>
 *
 * <h1><br/><a href="https://u.geekbang.org/subject/arch3/100614201">Java 业务架构实战营：https://u.geekbang.org/subject/arch3/100614201</a></h1><br/><h1><a href="https://gitee.com/ylimhhmily/BizArchPracticeCamp">Java 业务架构实战营代码地址：https://gitee.com/ylimhhmily/BizArchPracticeCamp</a></h1><br/><h1><a href="https://u.geekbang.org/subject/java4th/1001148?source=app_share">高级 Java 工程体系课 2023 版：https://u.geekbang.org/subject/java4th/1001148?source=app_share</a></h1><br/><h1><a href="https://time.geekbang.org/column/intro/100312101?tab=catalog">Dubbo 源码剖析与实战：https://time.geekbang.org/column/intro/100312101?tab=catalog</a></h1>
 *
 * @author hmilyylimh
 * ^_^
 * @version 0.0.1
 * ^_^
 * @date 2023/9/24 09:34
 */
public class RedisOperator {

    private static final RedisTemplate redisTemplate;

    static {
        redisTemplate = SpringCtxUtils.getBean(RedisTemplate.class);
    }

    /**
     * <h2>根据 key + value + 60 + TimeUnit.SECONDS 设置缓存。</h2>
     *
     * @throws
     * @param: key
     * @param: value
     * @return: void
     */
    public static void set(String key, @NotNull Object value) {
        set(key, value, 60, TimeUnit.SECONDS);
    }

    /**
     * <h2>根据 key + value + timeout + unit 设置缓存。</h2>
     *
     * @throws
     * @param: key
     * @param: value
     * @param: timeout
     * @param: unit
     * @return: void
     */
    public static void set(String key, @NotNull Object value, long timeout, TimeUnit unit) {
        ValueOperations<String, String> operations = redisTemplate.opsForValue();

        if (String.class.isAssignableFrom(value.getClass())) {
            operations.set(key, (String) value, timeout, unit);
        } else {
            operations.set(key, JacksonUtils.toJson(value), timeout, unit);
        }
    }

    /**
     * <h2>根据 key + hashKey + hashValue 设置 Map 缓存。</h2>
     *
     * @throws
     * @param: key
     * @param: value
     * @param: timeout
     * @param: unit
     * @return: void
     */
    public static void hset(String key, String hashKey, @NotNull Object hashValue) {
        HashOperations operations = redisTemplate.opsForHash();

        if (String.class.isAssignableFrom(hashValue.getClass())) {
            operations.put(key, hashKey, (String) hashValue);
        } else {
            operations.put(key, hashKey, JacksonUtils.toJson(hashValue));
        }
    }

    /**
     * <h2>根据 key 读取缓存值。</h2>
     *
     * @throws
     * @param: key
     * @return: java.lang.String
     */
    public static String get(String key) {
        return get(key, String.class);
    }

    /**
     * <h2>根据 key 读取缓存值并转为 clz 类型的对象。</h2>
     *
     * @param: key
     * @param: clz
     * @return: T
     */
    public static <T> T get(String key, Class<T> clz) {
        ValueOperations<String, String> operations = redisTemplate.opsForValue();
        Object result = operations.get(key);
        return result == null ? null : JacksonUtils.fromJson(result.toString(), clz);
    }

    /**
     * <h2>根据 key 读取缓存值并转为 clz 类型的对象。</h2>
     *
     * @param: key
     * @param: hashKey
     * @return: java.lang.String
     */
    public static String hget(String key, String hashKey) {
        return hget(key, hashKey, String.class);
    }

    /**
     * <h2>根据 key 读取缓存值并转为 clz 类型的对象。</h2>
     *
     * @param: key
     * @param: hashKey
     * @param: clz
     * @return: T
     */
    public static <T> T hget(String key, String hashKey, Class<T> clz) {
        HashOperations operations = redisTemplate.opsForHash();
        Object result = operations.get(key, hashKey);
        return result == null ? null : JacksonUtils.fromJson(result.toString(), clz);
    }

    /**
     * <h2>根据 key 读取缓存值并转为 clz 类型的对象。</h2>
     *
     * @param: key
     * @return: java.util.List<java.lang.String>
     */
    public static List<String> hgetAll(String key) {
        return hgetAll(key);
    }

    /**
     * <h2>根据 key 读取缓存值并转为 clz 类型的对象。</h2>
     *
     * @param: key
     * @param: clz
     * @return: java.util.List<T>
     */
    public static <T> List<T> hgetAll(String key, Class<T> clz) {
        // 先获取 key 下有多少 hashKeys
        HashOperations operations = redisTemplate.opsForHash();
        Set hashKeys = operations.keys(key);
        if (CollectionUtils.isEmpty(hashKeys)) {
            return null;
        }

        // 批量获取结果
        List valueList = operations.multiGet(key, hashKeys);
        if (CollectionUtils.isEmpty(valueList)) {
            return null;
        }

        // 批量解析为 clz 类型的结果
        List<T> resultList = new ArrayList(valueList.size());
        for (Object item : valueList) {
            T result = item == null ? null : JacksonUtils.fromJson(item.toString(), clz);
            resultList.add(result);
        }
        return resultList;
    }

    /**
     * <h2>根据 key + hashKeys 读取缓存值并转为字符串 List 输出。</h2>
     *
     * @param: key
     * @param: hashKeys
     * @return: java.util.List<java.lang.String>
     */
    public static List<String> multiGetList(String key, List<String> hashKeys) {
        return multiGetList(key, hashKeys, String.class);
    }


    /**
     * <h2>根据 key + hashKeys 读取缓存值并转为 clz 类型的 Map 对象。</h2>
     *
     * @param: key
     * @param: hashKeys
     * @return: java.util.List<java.lang.String>
     */
    public static Map<String, String> multiGetMap(String key, List<String> hashKeys) {
        return multiGetMap(key, hashKeys, String.class);
    }

    /**
     * <h2>根据 key + hashKeys 读取缓存值并转为 clz 类型的 List 对象。</h2>
     *
     * @param: key
     * @param: hashKeys
     * @param: clz
     * @return: java.util.List<T>
     */
    public static <T> List<T> multiGetList(String key, List<String> hashKeys, Class<T> clz) {
        // 如果入参为空，那就不用查询了
        if (CollectionUtils.isEmpty(hashKeys)) {
            return null;
        }

        // 批量获取结果
        HashOperations operations = redisTemplate.opsForHash();
        List valueList = operations.multiGet(key, hashKeys);
        if (CollectionUtils.isEmpty(valueList)) {
            return null;
        }

        // 批量解析为 clz 类型的结果
        List<T> resultList = new ArrayList(valueList.size());
        for (Object item : valueList) {
            T result = item == null ? null : JacksonUtils.fromJson(item.toString(), clz);
            resultList.add(result);
        }
        return resultList;
    }

    /**
     * <h2>根据 key + hashKeys 读取缓存值并转为 clz 类型的 Map 对象。</h2>
     *
     * @param: key
     * @param: hashKeys
     * @param: clz
     * @return: java.util.List<T>
     */
    public static <T> Map<String, T> multiGetMap(String key, List<String> hashKeys, Class<T> clz) {
        // 如果入参为空，那就不用查询了
        if (CollectionUtils.isEmpty(hashKeys)) {
            return null;
        }

        // 批量获取结果
        HashOperations operations = redisTemplate.opsForHash();
        List valueList = operations.multiGet(key, hashKeys);
        if (CollectionUtils.isEmpty(valueList)) {
            return null;
        }

        // 批量解析为 clz 类型的结果
        int len = hashKeys.size();
        Map<String, T> resultMap = new LinkedHashMap<>(len);
        for (int i = 0; i < len; i++) {
            Object item = valueList.get(i);
            T result = item == null ? null : JacksonUtils.fromJson(item.toString(), clz);
            resultMap.put(hashKeys.get(i), result);
        }
        return resultMap;
    }
}